import tkinter as tk
from tkinter import scrolledtext, ttk
import threading
import queue
from typing import Callable, Optional, Union

class Console:
    """控制台类，提供类似终端的功能，支持多窗口"""
    
    _instances = {}  # 存储所有控制台实例
    
    def __init__(self, title: str = "控制台", width: int = 800, height: int = 600, 
                 font: tuple = ("Consolas", 10), input_callback: Optional[Callable[[str], None]] = None):
        """
        初始化控制台
        
        参数:
            title: 窗口标题
            width: 窗口宽度
            height: 窗口高度
            font: 字体设置
            input_callback: 输入回调函数，当用户输入并回车时调用
        """
        self.title = title
        self.width = width
        self.height = height
        self.font = font
        self.input_callback = input_callback
        self.root = None
        self.text_area = None
        self.input_field = None
        self.close_button = None
        self.is_open = False
        self.input_queue = queue.Queue()
        self.output_queue = queue.Queue()
        self.thread = None
        self.id = id(self)
        Console._instances[self.id] = self
        
    def open(self):
        """打开控制台窗口"""
        if self.is_open:
            return
            
        self.thread = threading.Thread(target=self._run, daemon=True)
        self.thread.start()
        self.is_open = True
        
    def close(self):
        """关闭控制台窗口"""
        if not self.is_open or not self.root:
            return
            
        self.root.after(0, self.root.destroy)
        self.is_open = False
        if self.id in Console._instances:
            del Console._instances[self.id]
            
    def print(self, message: str, level: str = "info"):
        """
        向控制台输出信息
        
        参数:
            message: 要输出的消息
            level: 消息级别，可选值: info(默认), warning, error, success
        """
        self.output_queue.put((message, level))
        
    def _run(self):
        """在单独线程中运行GUI"""
        self.root = tk.Tk()
        self.root.title(self.title)
        self.root.geometry(f"{self.width}x{self.height}")
        self.root.configure(bg="black")
        
        # 创建主框架
        main_frame = ttk.Frame(self.root)
        main_frame.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
        
        # 创建文本显示区域
        self.text_area = scrolledtext.ScrolledText(main_frame, wrap=tk.WORD, 
                                                  bg="black", fg="#00FF00", 
                                                  insertbackground="#00FF00",
                                                  font=self.font)
        self.text_area.pack(fill=tk.BOTH, expand=True, side=tk.TOP, pady=(0, 5))
        self.text_area.config(state=tk.DISABLED)
        
        # 创建输入框和关闭按钮的框架
        input_frame = ttk.Frame(main_frame)
        input_frame.pack(fill=tk.X, side=tk.BOTTOM)
        
        # 创建输入框
        self.input_field = ttk.Entry(input_frame, font=self.font, foreground="#00FF00", background="black")
        self.input_field.pack(side=tk.LEFT, fill=tk.X, expand=True)
        self.input_field.bind("<Return>", self._on_input)
        self.input_field.focus_set()
        
        # 创建关闭按钮
        self.close_button = ttk.Button(input_frame, text="关闭", command=self.close)
        self.close_button.pack(side=tk.RIGHT, padx=(5, 0))
        
        # 设置颜色映射
        self.text_area.tag_config("info", foreground="#00FFFF")
        self.text_area.tag_config("warning", foreground="#FFFF00")
        self.text_area.tag_config("error", foreground="#FF0000")
        self.text_area.tag_config("success", foreground="#00FF00")
        
        # 启动更新循环
        self.root.after(100, self._update)
        self.root.mainloop()
        
    def _update(self):
        """更新控制台显示"""
        # 处理输出队列
        while not self.output_queue.empty():
            message, level = self.output_queue.get()
            self._append_text(message, level)
            
        # 继续更新
        if self.is_open and self.root:
            self.root.after(100, self._update)
            
    def _append_text(self, message: str, level: str):
        """向文本区域添加文本"""
        self.text_area.config(state=tk.NORMAL)
        self.text_area.insert(tk.END, message + "\n", level)
        self.text_area.see(tk.END)
        self.text_area.config(state=tk.DISABLED)
        
    def _on_input(self, event):
        """处理用户输入"""
        user_input = self.input_field.get()
        self.input_field.delete(0, tk.END)
        
        # 显示用户输入
        self._append_text(f"> {user_input}", "success")
        
        # 将输入传递给回调函数
        if self.input_callback:
            # 尝试将输入转换为ASCII值
            try:
                ascii_value = ord(user_input) if len(user_input) == 1 else int(user_input)
                self.input_callback(ascii_value)
            except ValueError:
                self.print(f"错误: 无法将 '{user_input}' 转换为ASCII值", "error")
        else:
            # 如果没有回调函数，将输入放入队列
            self.input_queue.put(user_input)
            
    @staticmethod
    def get_instance(instance_id: int) -> Optional['Console']:
        """获取指定ID的控制台实例"""
        return Console._instances.get(instance_id)
        
    @staticmethod
    def get_all_instances() -> dict:
        """获取所有控制台实例"""
        return Console._instances


# 使用示例
if __name__ == "__main__":
    # 定义input回调函数
    def handle_input(ascii_value: int):
        console1.print(f"收到ASCII值: {ascii_value} ({chr(ascii_value) if 0 <= ascii_value <= 127 else '?'})", "info")
        
    # 创建第一个控制台
    console1 = Console("控制台1", 600, 400, input_callback=handle_input)
    console1.open()
    console1.print("欢迎使用控制台1!", "success")
    console1.print("这是一个信息消息", "info")
    console1.print("这是一个警告消息", "warning")
    console1.print("这是一个错误消息", "error")
    
    # 创建第二个控制台
    console2 = Console("控制台2", 600, 400)
    console2.open()
    console2.print("控制台2已启动", "success")
    
    # 演示多线程输出
    import time
    import threading
    
    def background_task():
        count = 0
        while console1.is_open or console2.is_open:
            if console1.is_open:
                console1.print(f"后台任务计数: {count}", "info")
            if console2.is_open:
                console2.print(f"另一个控制台计数: {count}", "info")
            count += 1
            time.sleep(2)
    
    # 启动后台任务
    threading.Thread(target=background_task, daemon=True).start()
    
    # 保持主线程运行
    try:
        while any(console.is_open for console in Console._instances.values()):
            time.sleep(0.1)
    except KeyboardInterrupt:
        # 关闭所有控制台
        for console in list(Console._instances.values()):
            console.close()    